#ifndef __CHANNEL_TCP_included
#define __CHANNEL_TCP_included

#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include "Channel.h"
#include "../engine/PackageFactory.h"
#include "../engine/DebugStream.h"

using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
using namespace Engine;

class CChannelTCP : public CChannel {
	
	boost::shared_ptr<tcp::iostream> mSocket;
	std::string mRemoteAddr;
	int mRemotePort;

public :

	CChannelTCP(std::string& remote_addr, int remote_port) : 
	   mSocket(new tcp::iostream())
	{
		SetupRemote(remote_addr, remote_port);
	}

	virtual ~CChannelTCP() { }

	virtual void SetupLocal(int local_port) { }

	virtual void SetupRemote(std::string& remote_addr, int remote_port) {

		if(mSocket && mRemoteAddr == remote_addr && mRemotePort == remote_port) {
			return;
		}

		mRemoteAddr = remote_addr;
		mRemotePort = remote_port;
		DebugStream << "CChannelTCP - connect to " << remote_addr << ':' << remote_port << '\n';
		try {
			mSocket->connect(remote_addr, boost::lexical_cast<string>(remote_port));
		}
		catch(std::exception& e) {
			DebugStream << "CChannelTCP::SetupRemote - " << e.what() << '\n';
		}
	}

	virtual void ScheduleReceive(PackageReceiverRef receiver) {
		
		try {
			while(1) {
				PackageRef p = ReceivePackage();
				if(!p) continue;

				receiver->OnReceive(p);
			}
		}
		catch(std::exception& e) {
			DebugStream << "CChannelTCP - " << e.what() << '\n';
		}
	}

	virtual void Close() {
		DebugStream << "CChannelTCP - closed\n";
		mSocket->close();
		CChannel::Close();
	}

	virtual void Send(boost::shared_ptr<Engine::CPackage> p) {
		if(!mSocket->rdbuf()->is_open()) {
			DebugStream << "CChannelTCP - socket closed\n";
			return;
		}
		p->GetData(*mSocket);
	}

	void HandleReceiveTimer(const system::error_code& error) {
		//DebugStream << "CChannelTCP::HandleReceiveTimer - " << error.message() << '\n';
		if(error != error::operation_aborted) {
			mSocket->close();
		}
	}

	virtual boost::shared_ptr<CPackage> ReceivePackage() {
		if(!mSocket->rdbuf()->is_open()) {
			return PackageRef();
		}

		boost::asio::deadline_timer timer(mSocket->rdbuf()->get_io_service(), boost::posix_time::seconds(10*60));
		timer.async_wait(boost::bind(&CChannelTCP::HandleReceiveTimer, this, asio::placeholders::error));
		PackageRef p;
		p = CPackageFactory::Parse(mSocket, mRequestAllocator.get());
		timer.cancel();
		p->AddPropertie(string(PACKAGE_PROP_SERV_IP), mRemoteAddr);

		/*
		try {
			
		}
		catch(std::exception& e) {
			DebugStream << "CChannelTCP::ReceivePackage - " << e.what() << '\n';
			throw e;
		}
		*/

		return p;
	}

	virtual void GetLocalIPAddress(string& ip) {
		ip = mSocket->rdbuf()->local_endpoint().address().to_string();
	}

	virtual void GetRemoteIPAddress(string& ip) {
		ip = mRemoteAddr;
	}
};
#endif //__CHANNEL_TCP_included
